/**********************************************************************
 * Copyright (C) 2008 Andrew Khan
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package jxl.biff;

import jxl.Cell;
import jxl.CellType;
import jxl.LabelCell;
import jxl.Sheet;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Refactorisation to provide more sophisticated find cell by contents 
 * functionality
 */
public class CellFinder {
    private Sheet sheet;

    public CellFinder(Sheet s) {
        sheet = s;
    }

    /**
     * Gets the cell whose contents match the string passed in.
     * If no match is found, then null is returned.  The search is performed
     * on a row by row basis, so the lower the row number, the more
     * efficiently the algorithm will perform
     *
     * @param contents the string to match
     * @param firstCol the first column within the range
     * @param firstRow the first row of the range
     * @param lastCol the last column within the range
     * @param lastRow the last row within the range
     * @param reverse indicates whether to perform a reverse search or not
     * @return the Cell whose contents match the parameter, null if not found
     */
    public Cell findCell(String contents,
                         int firstCol,
                         int firstRow,
                         int lastCol,
                         int lastRow,
                         boolean reverse) {
        Cell cell = null;
        boolean found = false;

        int numCols = lastCol - firstCol;
        int numRows = lastRow - firstRow;

        int row1 = reverse ? lastRow : firstRow;
        int row2 = reverse ? firstRow : lastRow;
        int col1 = reverse ? lastCol : firstCol;
        int col2 = reverse ? firstCol : lastCol;
        int inc = reverse ? -1 : 1;

        for (int i = 0; i <= numCols && found == false; i++) {
            for (int j = 0; j <= numRows && found == false; j++) {
                int curCol = col1 + i * inc;
                int curRow = row1 + j * inc;
                if (curCol < sheet.getColumns() && curRow < sheet.getRows()) {
                    Cell c = sheet.getCell(curCol, curRow);
                    if (c.getType() != CellType.EMPTY) {
                        if (c.getContents().equals(contents)) {
                            cell = c;
                            found = true;
                        }
                    }
                }
            }
        }

        return cell;
    }

    /**
     * Finds a cell within a given range of cells
     *
     * @param contents the string to match
     * @return the Cell whose contents match the parameter, null if not found
     */
    public Cell findCell(String contents) {
        Cell cell = null;
        boolean found = false;

        for (int i = 0; i < sheet.getRows() && found == false; i++) {
            Cell[] row = sheet.getRow(i);
            for (int j = 0; j < row.length && found == false; j++) {
                if (row[j].getContents().equals(contents)) {
                    cell = row[j];
                    found = true;
                }
            }
        }

        return cell;
    }

    /**
     * Gets the cell whose contents match the regular expressionstring passed in.
     * If no match is found, then null is returned.  The search is performed
     * on a row by row basis, so the lower the row number, the more
     * efficiently the algorithm will perform
     *
     * @param pattern the regular expression string to match
     * @param firstCol the first column within the range
     * @param firstRow the first row of the range
     * @param lastCol the last column within the range
     * @param lastRow the last row within the range
     * @param reverse indicates whether to perform a reverse search or not
     * @return the Cell whose contents match the parameter, null if not found
     */
    public Cell findCell(Pattern pattern,
                         int firstCol,
                         int firstRow,
                         int lastCol,
                         int lastRow,
                         boolean reverse) {
        Cell cell = null;
        boolean found = false;

        int numCols = lastCol - firstCol;
        int numRows = lastRow - firstRow;

        int row1 = reverse ? lastRow : firstRow;
        int row2 = reverse ? firstRow : lastRow;
        int col1 = reverse ? lastCol : firstCol;
        int col2 = reverse ? firstCol : lastCol;
        int inc = reverse ? -1 : 1;

        for (int i = 0; i <= numCols && found == false; i++) {
            for (int j = 0; j <= numRows && found == false; j++) {
                int curCol = col1 + i * inc;
                int curRow = row1 + j * inc;
                if (curCol < sheet.getColumns() && curRow < sheet.getRows()) {
                    Cell c = sheet.getCell(curCol, curRow);
                    if (c.getType() != CellType.EMPTY) {
                        Matcher m = pattern.matcher(c.getContents());
                        if (m.matches()) {
                            cell = c;
                            found = true;
                        }
                    }
                }
            }
        }

        return cell;
    }

    /**
     * Gets the cell whose contents match the string passed in.
     * If no match is found, then null is returned.  The search is performed
     * on a row by row basis, so the lower the row number, the more
     * efficiently the algorithm will perform.  This method differs
     * from the findCell methods in that only cells with labels are
     * queried - all numerical cells are ignored.  This should therefore
     * improve performance.
     *
     * @param  contents the string to match
     * @return the Cell whose contents match the paramter, null if not found
     */
    public LabelCell findLabelCell(String contents) {
        LabelCell cell = null;
        boolean found = false;

        for (int i = 0; i < sheet.getRows() && !found; i++) {
            Cell[] row = sheet.getRow(i);
            for (int j = 0; j < row.length && !found; j++) {
                if ((row[j].getType() == CellType.LABEL ||
                        row[j].getType() == CellType.STRING_FORMULA) &&
                        row[j].getContents().equals(contents)) {
                    cell = (LabelCell) row[j];
                    found = true;
                }
            }
        }

        return cell;
    }
}
